﻿using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Web.Mvc.Html;
using Inovout.Descriptor;
using Inovout.Web.Mvc;

namespace System.Web.Mvc
{
    public static class HtmlHelperExtensions
    {
        public static ScopeHtmlHelper GetElementHtmlHelper<TModel>(this HtmlHelper<TModel> html, object element = null) where TModel : IEnumerable
        {
            //如果viewdaga.model不为空，则会重新创建ModelMetadata，之前所有对Metadata的设置复都将失效。
            var metadata = html.ViewData.ModelMetadata.AsWidgetCollectionModelMetadata().WidgetElementModelMetadata;
            var viewData = new WidgetDataDictionary(null, html.ViewData, metadata);
            if (element != null)
            {
                TypeDescriptor typeDescriptor = TypeDescriptor.Get(viewData.ModelDeclaringType);
                foreach (var property in metadata.Properties)
                {
                    if (typeDescriptor.Properties.Any(p => p.Name.Equals(property.PropertyName)))
                    {
                        //对于new的属性，一定要用new所在类型去赋值，不然会无效。
                        //由于上述原因，只能每个迭代都是赋值。
                        property.AsWidgetModelMetadata().SetModel(typeDescriptor.GetProperty(property.PropertyName).GetValue(element));
                    }
                    else
                    {
                        //表示扩展属性，不在Model中
                        property.AsWidgetModelMetadata().SetModel(element);
                    }
                }
            }
            var result = new ScopeHtmlHelper(html, viewData);
            //string expression = "Name";
            //ViewDataInfo vdi = viewData.GetViewDataInfo(expression);
            //var a0 = ModelMetadata.FromStringExpression(expression, result.ViewData);
            //var abcd = FromStringExpression(expression, result.ViewData, null);
            return result;
        }
        public static string BuildFirstOptionLabel<TModel>(this HtmlHelper<TModel> html, string optionInEditor, string optionInQuery, string separator = "-")
        {
            //因为DataSource必须从原始的ViewData获取，以便在迭代中始终使用原始的ViewData
            //另外一个重要原因HtmlHelper和HtmlHelper<T>中的ViewData不是同一个对象实例
     
            return BuildFirstOptionLabel(html, optionInEditor, optionInQuery, separator, html.ViewData);
        }
        private static string BuildFirstOptionLabel(this HtmlHelper html, string optionInEditor, string optionInQuery, string separator, ViewDataDictionary viewData)
        {
            bool result = html.ViewData.Model is QueryCriteriaModel;
            //if (html.ViewData.Model is QueryCriteriaModel)
            //{
            //    result = html.ViewData.GetQueryCriteria((IEnumerable)html.ViewData.Model) != null;
            //}
            if (result)
            {
                return BuildOptionLabel(viewData, optionInQuery, separator);
            }
            else
            {
                if (html.ViewData.ContainsKey(ScopeHtmlHelper.ScopeHtmlViewDataDictionaryKey))
                {
                    return BuildFirstOptionLabel(((ScopeHtmlHelper)html.ViewData[ScopeHtmlHelper.ScopeHtmlViewDataDictionaryKey]).Parent,
                        optionInEditor, optionInQuery, separator, viewData);
                }
                else
                {
                    return BuildOptionLabel(viewData, optionInEditor, separator);
                }
            }
        }
        private static string BuildOptionLabel(ViewDataDictionary viewData, string option, string separator)
        {
            IEnumerable<SelectListItem> selectList = viewData.ModelMetadata.ModelType.IsEnum?
                                viewData.GetDataSourceOfSelectList((Enum)viewData.Model) :
                                viewData.GetDataSourceOfSelectList();
            if (selectList == null || !selectList.Any() || selectList.Max(item => item.Text.Length) < option.Length)
            {
                return option;
            }
            int maxTextLength = selectList.Max(item => item.Text.Length) - option.Length;
            string optionFormatter = string.Empty;
            int cour = (int)(maxTextLength / 2);
            for (int i = 0; i < cour; i++)
            {
                optionFormatter += separator;
            }
            option = string.Format(optionFormatter + "{0}" + optionFormatter, option);

            if (maxTextLength % 2 != 0)
            {
                option = option + "-";
            }
            return option;
        }
        public static ScopeHtmlHelper GetModelHtmlHelper(this HtmlHelper html, WidgetModelMetadata metadata)
        {
            var viewData = new WidgetDataDictionary(html.ViewData, metadata);

            return new ScopeHtmlHelper(html, viewData);
        }
        internal static ModelMetadata FromStringExpression(string expression, ViewDataDictionary viewData, ModelMetadataProvider metadataProvider)
        {
            if (expression == null)
            {
                throw new ArgumentNullException("expression");
            }
            if (viewData == null)
            {
                throw new ArgumentNullException("viewData");
            }
            if (expression.Length == 0)
            {
                // Empty string really means "model metadata for the current model"
                return null;
            }

            ViewDataInfo vdi = viewData.GetViewDataInfo(expression);
            object container = null;
            Type containerType = null;
            Type modelType = null;
            Func<object> modelAccessor = null;
            string propertyName = null;

            if (vdi != null)
            {
                if (vdi.Container != null)
                {
                    container = vdi.Container;
                    containerType = vdi.Container.GetType();
                }

                modelAccessor = () => vdi.Value;

                if (vdi.PropertyDescriptor != null)
                {
                    propertyName = vdi.PropertyDescriptor.Name;
                    modelType = vdi.PropertyDescriptor.PropertyType;
                }
                else if (vdi.Value != null)
                {
                    // We only need to delay accessing properties (for LINQ to SQL)
                    modelType = vdi.Value.GetType();
                }
            }
            else if (viewData.ModelMetadata != null)
            {
                //  Try getting a property from ModelMetadata if we couldn't find an answer in ViewData
                ModelMetadata propertyMetadata = viewData.ModelMetadata.Properties.Where(p => p.PropertyName == expression).FirstOrDefault();
                if (propertyMetadata != null)
                {
                    return propertyMetadata;
                }
            }

            return null;
        }

        public static string GetExpressionText<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression)
        {
            return ExpressionHelper.GetExpressionText(expression);
        }
    }
}